package
{
	import flash.display.GradientType;
	import flash.display.Shape;
	import caurina.transitions.Tweener;
	import org.flintparticles.common.events.ParticleEvent;
	import org.flintparticles.common.actions.Age;
	import org.flintparticles.twoD.actions.Explosion;
	import org.flintparticles.twoD.particles.Particle2D;
	import flash.events.MouseEvent;
	import org.flintparticles.twoD.actions.SpeedLimit;
	import org.flintparticles.twoD.actions.Friction;
	import silin.geom.PathPoint;
	import silin.geom.Path;
	import silin.geom.FitLine;
	import flash.events.Event;
	import flash.geom.Point;
	import org.flintparticles.common.counters.Steady;
	import org.flintparticles.common.initializers.ImageClass;
	import org.flintparticles.twoD.actions.DeathZone;
	import org.flintparticles.twoD.actions.Move;
	import org.flintparticles.twoD.actions.RotateToDirection;
	import org.flintparticles.twoD.emitters.Emitter2D;
	import org.flintparticles.twoD.initializers.Position;
	import org.flintparticles.twoD.renderers.DisplayObjectRenderer;
	import org.flintparticles.twoD.zones.LineZone;
	import org.flintparticles.twoD.zones.RectangleZone;
	import waylines.actions.Antigravities;
	import waylines.waypoints.ActionResistance;
	import waylines.waypoints.FollowWaylines;
	import waylines.waypoints.Wayline;
	
	public class MainWaylines_2 extends MainWailines_1
	{
		protected var emitterWaylinesForMonsterArrows:Emitter2D;
		protected var emitterExplosion:Emitter2D;
		
		
		override protected function setup(e:Event=null):void
		{
			super.setup();
			
			// создаем новый эмиттер для крупных и медленных
			setupEmitterForMonsterArrows();
			// добавляем новый action к эмиттеру "для самых маленьких"
			// обратите внимание(!) эмиттер УЖЕ запущен, и его не надо перезапускать - поведение частиц можно менять на лету
			emitterWaylines.addAction( new Antigravities(emitterWaylinesForMonsterArrows, -400000) );
			
			setupEmitterForExplosion();
			// поскольку мы собираемся добавлять взрывы, нам уже нужна интерактивность
			stage.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
		}		
		/*
		 * настраиваем эмиттер для крупных стрелок
		 */
		protected function setupEmitterForMonsterArrows():void
		{
			var emitter:Emitter2D = new Emitter2D();
			// это счетчик - устанавливаем на 1 Чудовищную Стрелку в секунду
				emitter.counter = new Steady(1);
			
				var wayline:Wayline = _waylines[0];
				emitter.addInitializer( new Position( new LineZone( new Point(wayline.x - (wayline.radius-10)*Math.cos(wayline.rotation), wayline.y - (wayline.radius-10)*Math.sin(wayline.rotation)), new Point(wayline.x + (wayline.radius-10)*Math.cos(wayline.rotation), wayline.y + (wayline.radius-10)*Math.sin(wayline.rotation)) ) ) );
			// сообщаем, какую картинку использовать рендеру при отрисовке частицы
			// делаем юнитов покрупнее
				emitter.addInitializer( new ImageClass( Arrow, [10] ) );
				
				emitter.addAction( new DeathZone( new RectangleZone( -30, -30, stage.stageWidth+60, stage.stageHeight + 60 ), true ) );
				emitter.addAction( new Move() );
				emitter.addAction( new RotateToDirection() );
			// если юнитов этого типа будет мало, и между ними будет большое расстояние,
			// то можно было бы вообще исключить этот action	
			//	emitter.addAction( new MinimumDistance( 7, 600 ) );			
			
			// делаем юнитов помедленнее
				emitter.addAction( new ActionResistance(.1));
			
				emitter.addAction( new FollowWaylines(_waylines) );
		   	
		   	var	renderer:DisplayObjectRenderer = new DisplayObjectRenderer();
		   		addChild( renderer );
		   		renderer.addEmitter( emitter );
			// командуем старт
				emitterWaylinesForMonsterArrows = emitter;			
				emitterWaylinesForMonsterArrows.start();
		}
		override protected function setupWaylines():void
		{
			_waylines = [];
			
			var w:Number = stage.stageWidth;
			var h:Number = stage.stageHeight;
			var points:Array = [new Point(-9,h*.4), new Point(w*.3,h*.4), new Point(w*.5,h*.1), new Point(w*.8,h*.1), new Point(w*.8,h*.9), new Point(w*.5, h*.9), new Point(w*.3, h*.8), new Point(-40, h*.8)];
			var fitline:FitLine = new FitLine(points);
			var path:Path = new Path(fitline.fitPoints);
			
			/*
			 * переписываем одно чесло. Было 40, станет 25
			 * 
			 * более красивым решением, было бы написание метода, который расчитывал бы число шагов в зависимости от длины пути
			 * ну, это надо лишь, если мы планируем автоматически создавать много разных путей
			 */
			var step:Number = path.length / 25;
			
			var strength:Number = 100;
			for(var i:int=0; i<path.length; i+=step)
			{
				var segmentLength:int = 60;//+Math.random()*10;
				var pathpoint:PathPoint = path.getPathPoint(i);
				var wayline:Wayline = new Wayline(pathpoint.x, pathpoint.y, segmentLength, pathpoint.rotation-Math.PI/2, strength);
				_waylines.push(wayline);
			}
		}
		protected function setupEmitterForExplosion():void
		{
			var emitter:Emitter2D = new Emitter2D();
			// чтоб частицы двигались - это уже знакомо
				emitter.addAction( new Move() );
			// чтоб не играться с соотношениями сил, чтоб не очень быстро разбрасывались частицы - проще тупо ограничить скорость				
				emitter.addAction( new SpeedLimit(40));
			// это чтоб частицы постепенно замедлялись - трение
				emitter.addAction( new Friction(40) );
			// на всякий случай - вдруг вылетят (хотя можно было на другие эмиттеры оставить)
				emitter.addAction( new DeathZone( new RectangleZone( -30, -10, stage.stageWidth+40, stage.stageHeight + 20 ), true ) );
			// этот action отсчитывает "возраст" частицы. По истечению возраста, частица удаляется.
			// соотв. надо подписаться на событие, чтоб вернуть частицу в прежний эмиттер
				emitter.addAction( new Age() );
			// новый рендер
			var	renderer:DisplayObjectRenderer = new DisplayObjectRenderer();
		   		addChildAt( renderer, 0 );
		   		renderer.addEmitter( emitter );
			// командуем старт
				emitterExplosion = emitter;
				emitterExplosion.start();
			// подписываемся на "смерть частицы от старости", чтоб перенести ее обратно в "родной" эмиттер
			emitterExplosion.addEventListener(ParticleEvent.PARTICLE_DEAD, handleParticleDeadFromEmitterExplosion);
		}
		/*
		 * после смерти частицы в эмиттере взрывов, она снова оживает в своем первоначальном эмиттере
		 * проделываются манипуляции обратные тем, что поместили частицу в эмиттер взрывов
		 */
		protected function handleParticleDeadFromEmitterExplosion(e:ParticleEvent):void
		{
			// надо перевести частицу снова на "путь"
			var clone:Particle2D = e.particle.clone(emitterWaylines.particleFactory) as Particle2D;
				clone.image = new Arrow(4);
			/*
			 * обнулить параметр
			 * т.е. - частицу надо вернуть в прежний поток такой-же, какой взяли - чтоб не появилось какого-нибудь неожиданного поведения
			 */
				clone.angVelocity = 0;
			/*
			 * в эмиттере взрывов частица была помечена, как мертва. При клонировании, это свойство, естественно, тоже скопировалось
			 * поэтому нужно вручную "оживить" частицу, чтоб новый эмиттер сразу же ее не удалил 
			 */
				clone.isDead = false;
			// возвращаем частицу
				emitterWaylines.addParticle(clone);
		}
		protected function handleMouseDown(e:MouseEvent):void
		{
			// хоть намек на взрыв нарисуем
			var expl:Shape = new Shape();
				expl.graphics.beginGradientFill(GradientType.RADIAL, [0xff0000, 0], [1,.5], [127,255]);
				expl.graphics.drawCircle(0, 0, 10);
				expl.graphics.endFill();
				
				expl.x = e.stageX;
				expl.y = e.stageY;
				
				addChildAt(expl, 0);
				
			Tweener.addTween(expl, {time:1, alpha:.2, scaleX:6, scaleY:6, transition:"easeInOutBack", onComplete:function():void{expl.parent.removeChild(expl);}});
			
			Tweener.addCaller(this, {time:.5, count:1, onComplete:function():void{ explosion(e); }});
			//explosion(e);
		}
		// взрыв	
		protected function explosion(e:MouseEvent):void
		{	
			if(emitterWaylines == null){ return; }
			if(emitterExplosion == null){ return; }
			
			// радиус взрыва
			var explRadius:int = 30;
			// ради оптимизации заводим локальные переменные 
			// (внутри больших циклов обращение к данным не на прямую, а через dot-синтаксис начинает существенно потреблять процессорное время)
			var particleOrigin:Particle2D;
			var particleClone:Particle2D;
			var particlePoint:Point = new Point();
			
			// произошел взрыв в точке...
			var explPoint:Point = new Point(e.stageX, e.stageY);
			// готовимся к длинному циклу
			var particles:Array = emitterWaylines.particlesArray;
			var length:int = particles.length;
			// перебор всех частиц в эмиттере
			for(var p:int=0; p<length; p++)
			{
				particleOrigin = particles[p];
				particlePoint.x = particleOrigin.x;
				particlePoint.y = particleOrigin.y;
				// проверка, попадают ли частицы в радус действия взрыва
				if(Point.distance(explPoint, particlePoint) < explRadius)
				{
					/*
					 * клонируем частицу, которую накрыло взрывом - ее клон надо будет поместить в эмиттер взрывов
					 * и задаем ей небольшой импульс вращения - имитируем потерю контроля
					 */
					particleClone = particleOrigin.clone(emitterExplosion.particleFactory) as Particle2D;
					particleClone.angVelocity = -5 + Math.random() * 10;
					/*
					 * добавил две строки кода - лучше смотрится взрыв, если перед началом рачета обнулить скорость частицы
					 */
					particleClone.velX = 0;
					particleClone.velY = 0;
					/*
					 * action Age() в эмиттере взрывов, будет обрабатывать возраст частицы
					 * и когда возраст сравняется с заявенным временм жизни, она "умрет"
					 * тогда обработчик перехватит сообщение о смерти и перенесет частицу обратно
					 */
					particleClone.lifetime = 3;
					particleClone.age = 0;
					/*
					 * создаем новый экземпляр Arrow (красного цвета) - ведь объкты в ActionScript не копируются, а передается ссылка на них
					 * ВАЖНО! если копии не передать новую картинку, 
					 * то при удалении оригинальной частицы из прежнего эмиттера emitterWaylines сгенерится ошибка 
					 * - потому что рендер не сможет выполнить renderer.removeChild()
					 * 
					 * это ведь только прототип. И родной рендер используется только для визуализации процессов. 
					 * В реальной игре вы можете (и будете, наверняка) использовать сторонние рендеры, 
					 * и оперировать будете только координатами частиц (кстати - вот еще один важный пункт оптимизации) 
					 */
					particleClone.image = new Arrow(4, 0xff0000);
					// добавляем клонированную частицу в эмиттер взрывов
					emitterExplosion.addParticle(particleClone);
					// убираем частицы из старого эмиттера
					particleOrigin.isDead = true;
				}
			}
			
			/*
			 * добавляем action в эмиттер взрывов
			 * 
			 * на самом деле, конечно, подход неоднозначный - можно было бы сначала проверить, 
			 * зацепило ли кого взрывом, а потом уже создавать эмиттер и активировать его (т.е экономим на создании экзмпляра эмиттера).
			 * 
			 * с другой стороны - пришлось бы два цикла запускать: поиск и закгрузка в новый эмиттер
			 * 
			 * а может, в будущей игре взрывы возможны только в толпе, тогда первый вариант верный... 
			 * в общем - тут нужна комплексная оценка
			 */
			var explosion:Explosion = new Explosion(10000, explPoint.x, explPoint.y, 100);
			emitterExplosion.addAction(explosion);
			
			/*
			 * нам нужно чтоб взрыв воздействовал на частицу короткое время - чтоб ее не унесло за тридевять земель
			 * для этого надо ОДИН раз вызывать Emitter2D.update(.2) - чтоб частицы получили нужное ускорение
			 */			
			// задаем ускорение частицам в зоне взрыва внутри эмиттера
			emitterExplosion.update(0.2);
			// удаляем action Explosion  - он уже не нужен
			emitterExplosion.removeAction(explosion);			
		}
	}
}
